package com.zzw.config;

import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.cache.ehcache.EhCacheManager;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.filter.authc.AuthenticatingFilter;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import net.sf.ehcache.CacheManager;

import javax.servlet.Filter;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.Serializable;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * Shiro配置类
 * 用于配置Shiro相关的bean
 */
@Configuration
public class ShiroConfig {

    @Autowired
    private RedisSessionManager redisSessionManager;

    /**
     * 创建ShiroFilterFactoryBean
     * 用于配置Shiro的拦截规则
     */
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
        // 创建ShiroFilterFactoryBean对象
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();

        // 设置SecurityManager
        shiroFilterFactoryBean.setSecurityManager(securityManager);

        // 设置登录页面
        shiroFilterFactoryBean.setLoginUrl("/login");

        // 设置未授权页面
        shiroFilterFactoryBean.setUnauthorizedUrl("/unauthorized");

        // 配置拦截规则，使用LinkedHashMap保证顺序
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();

        // 配置不需要拦截的资源
        filterChainDefinitionMap.put("/", "anon");
        filterChainDefinitionMap.put("/login", "anon");
        filterChainDefinitionMap.put("/sysUser/login", "anon");
        filterChainDefinitionMap.put("/sysUser/captcha", "anon");
        filterChainDefinitionMap.put("/sysUser/register", "anon");
        filterChainDefinitionMap.put("/sysUser/logout", "anon");
        filterChainDefinitionMap.put("/api/email/**", "anon");
        filterChainDefinitionMap.put("/api/redis/**", "anon");
        filterChainDefinitionMap.put("/static/**", "anon");
        filterChainDefinitionMap.put("/favicon.ico", "anon");
        filterChainDefinitionMap.put("/error", "anon");
        filterChainDefinitionMap.put("/unauthorized", "anon");

        // Swagger相关资源放行
        filterChainDefinitionMap.put("/swagger-ui.html", "anon");
        filterChainDefinitionMap.put("/webjars/**", "anon");
        filterChainDefinitionMap.put("/swagger-resources/**", "anon");
        filterChainDefinitionMap.put("/v2/api-docs", "anon");

        // 将 authc 改为 user, 支持记住我功能
        // 认证或记住我可以访问(不拦截)
        filterChainDefinitionMap.put("/**", "user");

        filterChainDefinitionMap.put("/**", "anon"); // 所有请求都无需认证

        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);

        return shiroFilterFactoryBean;
    }

    /**
     * 创建DefaultWebSecurityManager
     * 安全管理器，用于管理所有Subject
     */
    @Bean
    public SecurityManager securityManager(CustomRealm customRealm, EhCacheManager ehCacheManager) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();

        // 设置Realm
        securityManager.setRealm(customRealm);

        // 设置缓存管理器
        securityManager.setCacheManager(ehCacheManager);

        // 设置记住我管理器
        securityManager.setRememberMeManager(rememberMeManager());

        // 设置会话管理器
        securityManager.setSessionManager(sessionManager());

        return securityManager;
    }

    /**
     * 创建会话管理器
     * 使用Redis存储会话
     */
    @Bean
    public DefaultWebSessionManager sessionManager() {
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        // 设置会话过期时间（毫秒）
        sessionManager.setGlobalSessionTimeout(1000 * 60 * 60 * 24);
        // 启用会话验证调度器
        sessionManager.setSessionValidationSchedulerEnabled(true);
        // 设置删除过期会话
        sessionManager.setDeleteInvalidSessions(true);
        // 设置会话ID Cookie
        sessionManager.setSessionIdCookie(sessionIdCookie());
        // 设置会话DAO
        sessionManager.setSessionDAO(redisSessionManager);

        // 开启Shiro序列化追踪
        sessionManager.isSessionValidationSchedulerEnabled();

        return sessionManager;
    }

    /**
     * 创建会话ID Cookie
     */
    @Bean
    public SimpleCookie sessionIdCookie() {
        SimpleCookie cookie = new SimpleCookie("SHIROSESSIONID");
        // 设置Cookie的路径
        cookie.setPath("/");
        // 设置Cookie的最大生命周期，单位：秒
        cookie.setMaxAge(60 * 60 * 24);// 默认为-1，即关闭浏览器时失效
        // 设置HttpOnly属性
        cookie.setHttpOnly(true);
        return cookie;
    }

    /**
     * 创建Realm
     * 自定义Realm，用于处理认证和授权
     * 方法参数 BCryptCredentialsMatcher bcryptCredentialsMatcher 是通过 Spring 的依赖注入机制自动传入的。但是要让 Spring 能够注入这个参数，首先需要在 Spring 的容器中存在一个 BCryptCredentialsMatcher 类型的 Bean。
     * 当Spring调用此方法创建CustomRealm Bean时，它会自动从容器中查找BCryptCredentialsMatcher类型的Bean并作为参数传入。
     */
    @Bean
    public CustomRealm customRealm(BCryptCredentialsMatcher bcryptCredentialsMatcher) {
        CustomRealm customRealm = new CustomRealm();
        // 设置自己的凭证匹配器
        customRealm.setCredentialsMatcher(bcryptCredentialsMatcher);
        return customRealm;
    }

    /**
     * 创建EhCacheManager
     * 缓存管理器，用于缓存认证和授权数据
     */
    @Bean
    public EhCacheManager ehCacheManager() {
        EhCacheManager ehCacheManager = new EhCacheManager();

        // 使用静态工厂方法创建CacheManager，避免多实例问题
        net.sf.ehcache.CacheManager cacheManager = net.sf.ehcache.CacheManager.getCacheManager("shiro");
        if (cacheManager == null) {
            try {
                cacheManager = net.sf.ehcache.CacheManager.create(
                    getClass().getClassLoader().getResourceAsStream("ehcache.xml")
                );
                cacheManager.setName("shiro");
            } catch (Exception e) {
                throw new RuntimeException("初始化EhCache CacheManager失败", e);
            }
        }
        ehCacheManager.setCacheManager(cacheManager);

        return ehCacheManager;
    }

    /**
     * 开启Shiro注解支持
     * 用于支持@RequiresRoles、@RequiresPermissions等注解
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(securityManager);
        return advisor;
    }

    @Bean
    public CookieRememberMeManager rememberMeManager() {
        CookieRememberMeManager rememberMeManager = new CookieRememberMeManager();
        // 设置Cookie参数
        SimpleCookie cookie = new SimpleCookie("rememberMe");
        cookie.setMaxAge(86400); // 有效期1天
        rememberMeManager.setCookie(cookie);
        return rememberMeManager;
    }
}
